package com.test_wakeup;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import android.content.Context;
import android.os.PowerManager;

public class Connection implements Runnable {
	TestConnectionT activity;

	enum RequestType {
		None, Open, Close, Stop
	}

	InetAddress addr;
	int port;

	SocketChannel sch;
	Selector selector;

	RequestType request = RequestType.None;

	Connection(TestConnectionT activity) {
		this.activity = activity;
	}

	public void run() {
		int nSelected = 0;
		boolean exit = false;
		boolean wait_on_select = false;

		try {
			selector = Selector.open();
		} catch (final IOException ex) {
			activity.runOnUiThread(new Runnable() {

				@Override
				public void run() {
					activity.reportException(ex);
				}
			});
		}

		while (!exit) {
			try {

				synchronized (this) {
					switch (request) {
					case None:
						break;
					case Open:
						if (sch == null)
							executeOpen();
						break;
					case Stop:
						exit = true;
					case Close:
						if (sch != null)
							executeClose(selector);
						nSelected = 0;
						break;
					}
					if (exit)
						continue;
					
					request = RequestType.None;
					if (sch != null && nSelected != 0)
						read_write();
				
					if (sch == null) {
						wait();
						wait_on_select = false;
						nSelected = 0;
					} else { 
						wait_on_select = true;
					}
				}
				if (wait_on_select) {
					nSelected = selector.select();
				}
			} catch (final Exception ex) {
				activity.runOnUiThread(new Runnable() {

					@Override
					public void run() {
						activity.reportException(ex);
					}
				});
				request = RequestType.None;
			}
		}
	}

	public synchronized void stop() {
		if (sch == null) {
			notify();
		} else {
			selector.wakeup();
		}
		request = RequestType.Stop;
	}

	public synchronized void open(InetAddress addr, int port)
			throws IOException {
		this.addr = addr;
		this.port = port;
		request = RequestType.Open;
		notify();
	}

	public synchronized void executeOpen() throws IOException {
		try {
			sch = SocketChannel.open();
			sch.configureBlocking(false);
			if (sch.connect(new InetSocketAddress(addr, port))) {
				sch.register(selector, SelectionKey.OP_READ);
			} else {
				sch.register(selector, SelectionKey.OP_CONNECT);
			}
		} catch (IOException ex) {
			try {
				sch.close();
			} finally {
				sch = null;
			}

			activity.runOnUiThread(new Runnable() {

				@Override
				public void run() {
					activity.setDisconnected();
				}
			});
			throw ex;
		}

	}

	public synchronized void close() {
		if (sch == null)
			return;
		
		request = RequestType.Close;
		selector.wakeup();
	}

	void read_write() throws Exception {
		Set<SelectionKey> selectedKeys = selector.selectedKeys();
		if (selectedKeys.size() != 1)
			throw new Exception("Unexpected selectedKeys size "
					+ selectedKeys.size());

		Iterator<SelectionKey> it = selector.selectedKeys().iterator();
        while (it.hasNext()) {
        	SelectionKey key = (SelectionKey) it.next();
        	it.remove();
        	
			if (key.isReadable())
			{
				ByteBuffer target = ByteBuffer.allocate(1024);
				int rc;
				
				do {
					rc = sch.read(target);
					if (rc > 0) {
						target.flip();
						byte [] bytes = new byte[target.limit()];
						target.get(bytes);
						for (byte b : bytes) {
							if (b == 'w') {
								wakeup_screen();
							}
						}
						target.clear();
					}
				} while (rc == target.capacity());
					
				if (rc == -1) 
				{
					executeClose(selector);
					return;
				}
			}
			if (key.isConnectable()) {
				try {
					sch.finishConnect();
					sch.register(selector, SelectionKey.OP_READ);
					activity.runOnUiThread(new Runnable() {

						@Override
						public void run() {
							activity.setConnected();
						}
					});
				} catch (final IOException ex) {
					executeClose(selector);
					activity.runOnUiThread(new Runnable() {

						@Override
						public void run() {
							activity.reportException(ex);
						}
					});
					
					
				}
			}
		}

	}

	void executeClose(Selector selector)  throws IOException {
		activity.runOnUiThread(new Runnable() {

			@Override
			public void run() {
				activity.setDisconnected();
			}
		});
		try {
			sch.close();
			selector.selectNow();
		} finally {
			sch = null;
		}
	}
	
	void wakeup_screen()
	{
		PowerManager pm = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
		 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "My Tag");
		 wl.acquire();
	}
	

}
